1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.cache;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.collect.ImmutableList;
21  import com.google.common.collect.ImmutableMap;
22  import com.google.common.collect.ImmutableSet;
23  import com.google.common.collect.Sets;
24  import com.google.common.testing.FakeTicker;
25  
26  import junit.framework.TestCase;
27  
28  import java.util.Iterator;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.Set;
32  import java.util.concurrent.Callable;
33  import java.util.concurrent.ConcurrentMap;
34  import java.util.concurrent.ExecutionException;
35  import java.util.concurrent.TimeUnit;
36  
37  /**
38   * Test suite for {@link CacheBuilder}.
39   * TODO(cpovirk): merge into CacheBuilderTest?
40   *
41   * @author Jon Donovan
42   */
43  @GwtCompatible
44  public class CacheBuilderGwtTest extends TestCase {
45  
46    private FakeTicker fakeTicker;
47  
48    @Override
49    protected void setUp() throws Exception {
50      super.setUp();
51  
52      fakeTicker = new FakeTicker();
53    }
54  
55    public void testLoader() throws ExecutionException {
56  
57      final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
58          .build();
59  
60      Callable<Integer> loader = new Callable<Integer>() {
61        private int i = 0;
62  
63        @Override
64        public Integer call() throws Exception {
65          return ++i;
66        }
67      };
68  
69      cache.put(0, 10);
70  
71      assertEquals(Integer.valueOf(10), cache.get(0, loader));
72      assertEquals(Integer.valueOf(1), cache.get(20, loader));
73      assertEquals(Integer.valueOf(2), cache.get(34, loader));
74  
75      cache.invalidate(0);
76      assertEquals(Integer.valueOf(3), cache.get(0, loader));
77  
78      cache.put(0, 10);
79      cache.invalidateAll();
80      assertEquals(Integer.valueOf(4), cache.get(0, loader));
81    }
82  
83    public void testSizeConstraint() {
84      final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
85          .maximumSize(4)
86          .build();
87  
88      cache.put(1, 10);
89      cache.put(2, 20);
90      cache.put(3, 30);
91      cache.put(4, 40);
92      cache.put(5, 50);
93  
94      assertEquals(null, cache.getIfPresent(10));
95      // Order required to remove dependence on acces order / write order constraint.
96      assertEquals(Integer.valueOf(20), cache.getIfPresent(2));
97      assertEquals(Integer.valueOf(30), cache.getIfPresent(3));
98      assertEquals(Integer.valueOf(40), cache.getIfPresent(4));
99      assertEquals(Integer.valueOf(50), cache.getIfPresent(5));
100 
101     cache.put(1, 10);
102     assertEquals(Integer.valueOf(10), cache.getIfPresent(1));
103     assertEquals(Integer.valueOf(30), cache.getIfPresent(3));
104     assertEquals(Integer.valueOf(40), cache.getIfPresent(4));
105     assertEquals(Integer.valueOf(50), cache.getIfPresent(5));
106     assertEquals(null, cache.getIfPresent(2));
107   }
108   
109   public void testLoadingCache() throws ExecutionException {
110     CacheLoader<Integer, Integer> loader = new CacheLoader<Integer, Integer>() {
111       int i = 0;
112       @Override
113       public Integer load(Integer key) throws Exception {
114         return i++;
115       }
116       
117     };
118     
119     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
120         .build(loader);
121     
122     cache.put(10, 20);
123     
124     Map<Integer, Integer> map = cache.getAll(ImmutableList.of(10, 20, 30, 54, 443, 1));
125 
126     assertEquals(Integer.valueOf(20), map.get(10));
127     assertEquals(Integer.valueOf(0), map.get(20));
128     assertEquals(Integer.valueOf(1), map.get(30));
129     assertEquals(Integer.valueOf(2), map.get(54));
130     assertEquals(Integer.valueOf(3), map.get(443));
131     assertEquals(Integer.valueOf(4), map.get(1));
132     assertEquals(Integer.valueOf(5), cache.get(6));
133     assertEquals(Integer.valueOf(6), cache.apply(7));
134   }
135 
136   public void testExpireAfterAccess() {
137     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
138         .expireAfterAccess(1000, TimeUnit.MILLISECONDS)
139         .ticker(fakeTicker)
140         .build();
141 
142     cache.put(0, 10);
143     cache.put(2, 30);
144 
145     fakeTicker.advance(999, TimeUnit.MILLISECONDS);
146     assertEquals(Integer.valueOf(30), cache.getIfPresent(2));
147     fakeTicker.advance(1, TimeUnit.MILLISECONDS);
148     assertEquals(Integer.valueOf(30), cache.getIfPresent(2));
149     fakeTicker.advance(1000, TimeUnit.MILLISECONDS);
150     assertEquals(null, cache.getIfPresent(0));
151   }
152 
153   public void testExpireAfterWrite() {
154     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
155         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
156         .ticker(fakeTicker)
157         .build();
158 
159     cache.put(10, 100);
160     cache.put(20, 200);
161     cache.put(4, 2);
162 
163     fakeTicker.advance(999, TimeUnit.MILLISECONDS);
164     assertEquals(Integer.valueOf(100), cache.getIfPresent(10));
165     assertEquals(Integer.valueOf(200), cache.getIfPresent(20));
166     assertEquals(Integer.valueOf(2), cache.getIfPresent(4));
167 
168     fakeTicker.advance(2, TimeUnit.MILLISECONDS);
169     assertEquals(null, cache.getIfPresent(10));
170     assertEquals(null, cache.getIfPresent(20));
171     assertEquals(null, cache.getIfPresent(4));
172 
173     cache.put(10, 20);
174     assertEquals(Integer.valueOf(20), cache.getIfPresent(10));
175 
176     fakeTicker.advance(1000, TimeUnit.MILLISECONDS);
177     assertEquals(null, cache.getIfPresent(10));
178   }
179 
180   public void testExpireAfterWriteAndAccess() {
181     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
182         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
183         .expireAfterAccess(500, TimeUnit.MILLISECONDS)
184         .ticker(fakeTicker)
185         .build();
186 
187     cache.put(10, 100);
188     cache.put(20, 200);
189     cache.put(4, 2);
190 
191     fakeTicker.advance(499, TimeUnit.MILLISECONDS);
192     assertEquals(Integer.valueOf(100), cache.getIfPresent(10));
193     assertEquals(Integer.valueOf(200), cache.getIfPresent(20));
194 
195     fakeTicker.advance(2, TimeUnit.MILLISECONDS);
196     assertEquals(Integer.valueOf(100), cache.getIfPresent(10));
197     assertEquals(Integer.valueOf(200), cache.getIfPresent(20));
198     assertEquals(null, cache.getIfPresent(4));
199 
200     fakeTicker.advance(499, TimeUnit.MILLISECONDS);
201     assertEquals(null, cache.getIfPresent(10));
202     assertEquals(null, cache.getIfPresent(20));
203 
204     cache.put(10, 20);
205     assertEquals(Integer.valueOf(20), cache.getIfPresent(10));
206 
207     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
208     assertEquals(null, cache.getIfPresent(10));
209   }
210 
211   public void testMapMethods() {
212     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
213         .build();
214 
215     ConcurrentMap<Integer, Integer> asMap = cache.asMap();
216 
217     cache.put(10, 100);
218     cache.put(2, 52);
219 
220     asMap.replace(2, 79);
221     asMap.replace(3, 60);
222 
223     assertEquals(null, cache.getIfPresent(3));
224     assertEquals(null, asMap.get(3));
225 
226     assertEquals(Integer.valueOf(79), cache.getIfPresent(2));
227     assertEquals(Integer.valueOf(79), asMap.get(2));
228 
229     asMap.replace(10, 100, 50);
230     asMap.replace(2, 52, 99);
231 
232     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
233     assertEquals(Integer.valueOf(50), asMap.get(10));
234     assertEquals(Integer.valueOf(79), cache.getIfPresent(2));
235     assertEquals(Integer.valueOf(79), asMap.get(2));
236 
237     asMap.remove(10, 100);
238     asMap.remove(2, 79);
239 
240     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
241     assertEquals(Integer.valueOf(50), asMap.get(10));
242     assertEquals(null, cache.getIfPresent(2));
243     assertEquals(null, asMap.get(2));
244 
245     asMap.putIfAbsent(2, 20);
246     asMap.putIfAbsent(10, 20);
247 
248     assertEquals(Integer.valueOf(20), cache.getIfPresent(2));
249     assertEquals(Integer.valueOf(20), asMap.get(2));
250     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
251     assertEquals(Integer.valueOf(50), asMap.get(10));
252   }
253 
254   public void testRemovalListener() {
255     final int[] stats = new int[4];
256 
257     RemovalListener<Integer, Integer> countingListener = new RemovalListener<Integer, Integer>() {
258       @Override
259       public void onRemoval(RemovalNotification<Integer, Integer> notification) {
260         switch (notification.getCause()) {
261           case EXPIRED:
262             stats[0]++;
263             break;
264           case EXPLICIT:
265             stats[1]++;
266             break;
267           case REPLACED:
268             stats[2]++;
269             break;
270           case SIZE:
271             stats[3]++;
272             break;
273           default:
274             throw new IllegalStateException("No collected exceptions in GWT CacheBuilder.");
275         }
276       }
277     };
278 
279     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
280         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
281         .removalListener(countingListener)
282         .ticker(fakeTicker)
283         .maximumSize(2)
284         .build();
285 
286     // Add more than two elements to increment size removals.
287     cache.put(3, 20);
288     cache.put(6, 2);
289     cache.put(98, 45);
290     cache.put(56, 76);
291     cache.put(23, 84);
292 
293     // Replace the two present elements.
294     cache.put(23, 20);
295     cache.put(56, 49);
296     cache.put(23, 2);
297     cache.put(56, 4);
298 
299     // Expire the two present elements.
300     fakeTicker.advance(1001, TimeUnit.MILLISECONDS);
301 
302     cache.getIfPresent(23);
303     cache.getIfPresent(56);
304 
305     // Add two elements and invalidate them.
306     cache.put(1, 4);
307     cache.put(2, 8);
308 
309     cache.invalidateAll();
310 
311     assertEquals(2, stats[0]);
312     assertEquals(2, stats[1]);
313     assertEquals(4, stats[2]);
314     assertEquals(3, stats[3]);
315   }
316   
317   public void testPutAll() {
318     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
319         .build();
320     
321     cache.putAll(ImmutableMap.of(10, 20, 30, 50, 60, 90));
322 
323     assertEquals(Integer.valueOf(20), cache.getIfPresent(10));
324     assertEquals(Integer.valueOf(50), cache.getIfPresent(30));
325     assertEquals(Integer.valueOf(90), cache.getIfPresent(60));
326     
327     cache.asMap().putAll(ImmutableMap.of(10, 50, 30, 20, 60, 70, 5, 5));
328 
329     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
330     assertEquals(Integer.valueOf(20), cache.getIfPresent(30));
331     assertEquals(Integer.valueOf(70), cache.getIfPresent(60));
332     assertEquals(Integer.valueOf(5), cache.getIfPresent(5));
333   }
334   
335   public void testInvalidate() {
336     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
337         .build();
338     
339     cache.put(654, 2675);
340     cache.put(2456, 56);
341     cache.put(2, 15);
342     
343     cache.invalidate(654);
344 
345     assertFalse(cache.asMap().containsKey(654));
346     assertTrue(cache.asMap().containsKey(2456));
347     assertTrue(cache.asMap().containsKey(2));
348   }
349   
350   public void testInvalidateAll() {
351     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
352         .build();
353     
354     cache.put(654, 2675);
355     cache.put(2456, 56);
356     cache.put(2, 15);
357     
358     cache.invalidateAll();
359     assertFalse(cache.asMap().containsKey(654));
360     assertFalse(cache.asMap().containsKey(2456));
361     assertFalse(cache.asMap().containsKey(2));
362     
363     cache.put(654, 2675);
364     cache.put(2456, 56);
365     cache.put(2, 15);
366     cache.put(1, 3);
367     
368     cache.invalidateAll(ImmutableSet.of(1, 2));
369 
370     assertFalse(cache.asMap().containsKey(1));
371     assertFalse(cache.asMap().containsKey(2));
372     assertTrue(cache.asMap().containsKey(654));
373     assertTrue(cache.asMap().containsKey(2456));
374   }
375   
376   public void testAsMap_containsValue() {
377     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
378         .expireAfterWrite(20000, TimeUnit.MILLISECONDS)
379         .ticker(fakeTicker)
380         .build();
381     
382     cache.put(654, 2675);
383     fakeTicker.advance(10000, TimeUnit.MILLISECONDS);
384     cache.put(2456, 56);
385     cache.put(2, 15);
386     
387     fakeTicker.advance(10001, TimeUnit.MILLISECONDS);
388 
389     assertTrue(cache.asMap().containsValue(15));
390     assertTrue(cache.asMap().containsValue(56));
391     assertFalse(cache.asMap().containsValue(2675));
392   }
393   
394   public void testAsMap_containsKey() {
395     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
396         .expireAfterWrite(20000, TimeUnit.MILLISECONDS)
397         .ticker(fakeTicker)
398         .build();
399     
400     cache.put(654, 2675);
401     fakeTicker.advance(10000, TimeUnit.MILLISECONDS);
402     cache.put(2456, 56);
403     cache.put(2, 15);
404     
405     fakeTicker.advance(10001, TimeUnit.MILLISECONDS);
406 
407     assertTrue(cache.asMap().containsKey(2));
408     assertTrue(cache.asMap().containsKey(2456));
409     assertFalse(cache.asMap().containsKey(654));
410   }
411   
412   public void testAsMapValues_contains() {
413     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
414         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
415         .ticker(fakeTicker)
416         .build();
417     
418     cache.put(10, 20);
419     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
420     cache.put(20, 22);
421     cache.put(5, 10);
422     
423     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
424 
425     assertTrue(cache.asMap().values().contains(22));
426     assertTrue(cache.asMap().values().contains(10));
427     assertFalse(cache.asMap().values().contains(20));
428   }
429   
430   public void testAsMapKeySet() {
431     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
432         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
433         .ticker(fakeTicker)
434         .build();
435     
436     cache.put(10, 20);
437     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
438     cache.put(20, 22);
439     cache.put(5, 10);
440     
441     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
442 
443     Set<Integer> foundKeys = Sets.newHashSet();
444     for (Integer current : cache.asMap().keySet()) {
445       foundKeys.add(current);
446     }
447 
448     assertEquals(ImmutableSet.of(20, 5), foundKeys);
449   }
450   
451 
452   public void testAsMapKeySet_contains() {
453     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
454         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
455         .ticker(fakeTicker)
456         .build();
457     
458     cache.put(10, 20);
459     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
460     cache.put(20, 22);
461     cache.put(5, 10);
462     
463     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
464 
465     assertTrue(cache.asMap().keySet().contains(20));
466     assertTrue(cache.asMap().keySet().contains(5));
467     assertFalse(cache.asMap().keySet().contains(10));
468   }
469   
470   public void testAsMapEntrySet() {
471     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
472         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
473         .ticker(fakeTicker)
474         .build();
475     
476     cache.put(10, 20);
477     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
478     cache.put(20, 22);
479     cache.put(5, 10);
480     
481     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
482     
483     int sum = 0;
484     for (Entry<Integer, Integer> current : cache.asMap().entrySet()) {
485       sum += current.getKey() + current.getValue();
486     }
487     assertEquals(57, sum);
488   }
489   
490   public void testAsMapValues_iteratorRemove() {
491     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
492         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
493         .ticker(fakeTicker)
494         .build();
495     
496     cache.put(10, 20);
497     Iterator<Integer> iterator = cache.asMap().values().iterator();
498     iterator.next();
499     iterator.remove();
500     
501     assertEquals(0, cache.size());
502   }
503 }